﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using MessageServer.utilities;

namespace MessageServer.channels.socket
{
    /**
     * This interface has to be implemented to achieve different types of
     * streaming functionality. Currently only a chunkedstreaming is done.
     * */
    public interface SocketStream
    {
        /**
         * Read the given channel and return the data received in it
         * as a bytebuffer.
         * */
        ByteBuffer read();

        /**
         * Write the data in the bytebuffer passed into the given channel
         * */
        void write(ByteBuffer buff);
    }

    /**
     * This class allows for chunked read and write from a socket. This will chunk the data as 1K blocks
     * and try to read and write to it.
     * */
    public class ChunkedStream : SocketStream
    {
        const int CHUNK_SIZE = 1024;    //The chunk size for each read and write
        Socket _socket;                 //the socket into which to write and read

        //Constructor which dictates the socket into which the data is streamed
        public ChunkedStream(Socket sock)
        {
            _socket = sock;
        }

        /**
         * The data available on the socket is read @1024 bytes per read and appends it into the
         * bytebuffer and this is returned.
         * */
        public ByteBuffer read()
        {
            int nobytes = _socket.Available;
            ChannelLogger.LOG.LogDebug("ChunkedStream:read:Reading " + nobytes + " from socket");
            ByteBuffer buffer = new ByteBuffer(CHUNK_SIZE); //increase by 1K
            byte[] tst = new byte[CHUNK_SIZE];
            int read = _socket.Receive(tst, CHUNK_SIZE, SocketFlags.None);
            buffer.Append(tst, 0, read);
            nobytes -= read;
            while (nobytes > 0)
            {
                read = _socket.Receive(tst, CHUNK_SIZE, SocketFlags.None);
                buffer.Append(tst, 0, read);
                nobytes -= read;
            }
            System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
            //ChannelLogger.LOG.LogDebug("ChunkedStream:read:Read bytes " + enc.GetString(tst));
            return buffer;
        }

        /**
         * Write the data in the ByteBuffer in chunks of 1K till all data is sent.
         * */
        public void write(ByteBuffer buffer)
        {
            byte[] data = buffer.getData();
            int nobytes = data.Length;
            ChannelLogger.LOG.LogDebug("ChunkedStream:write:Writing:" + nobytes);
            System.Text.UTF8Encoding enc = new System.Text.UTF8Encoding();
            //ChannelLogger.LOG.LogDebug("ChunkedStream:write:writing bytes " + enc.GetString(data));
            int written = 0;
            while (written < nobytes)
            {
                int writebytes = (nobytes - written);
                if (writebytes > CHUNK_SIZE) writebytes = CHUNK_SIZE; //limit to the size sent
                int sent = _socket.Send(data, written, writebytes, SocketFlags.None);
                written += sent;
                ChannelLogger.LOG.LogDebug("ChunkedStream:write:writing bytes " + written);
            }
        }
    }

}
